﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; 
using System.Linq.Expressions;

namespace NMoq.Core
{
    public class Mock<T>
    {
        #region properties
        public T Target 
        {
            get
            {
                return MockProxy.Target ;
            }
        }

        public T ProxiedTarget
        {
            get
            {
                return MockProxy.ProxiedTarget;
            }
        }

        protected MockProxy<T> MockProxy
        {
            get;
            set;
        }
        #endregion

        #region constructors
        public Mock(params object[] args)
        {
            MockProxy = MockProxyFactory.CreateProxy<T>(args); 
        }

        public Mock(InterceptorActionAfterRead actoinAfterRead, params object[] args)
        {
            MockProxy = MockProxyFactory.CreateProxy<T>(args);
            MockProxy.ActionAfterRead = actoinAfterRead;
        }
        #endregion

        #region setup method
        public T SetupActionAfterRead(InterceptorActionAfterRead actionAfterRead)
        {
            MockProxy.ActionAfterRead = actionAfterRead;
            return ProxiedTarget;
        }
        #endregion

        #region call selector methods
        public void CallSelector<T1>(Expression<Action<Selector<T1>>> expression) where T1:IAction 
        {
            MockProxy.CallSelector<T1>(expression);
        }

        public TResult CallSelector<T1, TResult>(Expression<Func<Selector<T1>, TResult>> expression) where T1 : IAction 
        {
            return MockProxy.CallSelector<T1, TResult>(expression);
        }
        #endregion

        #region assistant methods
        private void SetupDefaultAction<TAction>(MethodMessageInfo methodMessageInfo, bool coverExist)
            where TAction :IAction 
        {
            TAction actionTarget = CallSelector<TAction, TAction>(i => i.Select(methodMessageInfo));
            if (coverExist || (!coverExist && actionTarget == null))
                CallSelector<TAction>(i => i.Setup(methodMessageInfo));
        }
 
        #endregion

        #region mock Action methods
        public  void MockActionMethod(Expression<Action<T>> expression)
        {
            MethodMessageInfo methodMessageInfo = MethodMessageInfo.CreateMethodMessageInfo<T>(expression, Target);
            CallSelector<IInterceptor>(i => i.Setup(methodMessageInfo));
            CallSelector<IDataProvider>(s => s.Setup(methodMessageInfo));
            CallSelector<IVerifier>(v => v.Setup(methodMessageInfo));
        }

        public void MockActionMethod<TAction>(Expression<Action<T>> expression, TAction actionTarget)
            where TAction : IAction
        {
            MethodMessageInfo methodMessageInfo = MethodMessageInfo.CreateMethodMessageInfo<T>(expression, Target);
            CallSelector<TAction>(i => i.Setup(methodMessageInfo, actionTarget));

            if (typeof(TAction) != typeof(IInterceptor))
                SetupDefaultAction<IInterceptor>(methodMessageInfo, false);
        }

        public void MockActionMethod<TAction>(Expression<Action<T>> expression, Action<MethodMessageInfo, TAction> action)
            where TAction : IAction
        {            
            MethodMessageInfo methodMessageInfo = MethodMessageInfo.CreateMethodMessageInfo<T>(expression, Target);
            CallSelector<TAction>(s => s.Setup(methodMessageInfo));
            CallSelector<TAction>(s => s.CallDefaultAction(methodMessageInfo, action));

            if (typeof(TAction) != typeof(IInterceptor))
                SetupDefaultAction<IInterceptor>(methodMessageInfo, false);
        }

        public void MockActionMethod<TAction>(Expression<Action<T>> expression, Func<MethodMessageInfo, IEnumerable<TAction>, TAction> selectFunc)
            where TAction : IAction
        {            
            MethodMessageInfo methodMessageInfo = MethodMessageInfo.CreateMethodMessageInfo<T>(expression, Target);
            CallSelector<TAction>(s => s.Setup(methodMessageInfo, selectFunc));

            if (typeof(TAction) != typeof(IInterceptor))
                SetupDefaultAction<IInterceptor>(methodMessageInfo, false);
        }
        #endregion

        #region mock Func methods
        public  void MockFuncMethod<TResult>(Expression<Func<T, TResult>> expression)
        {            
            MethodMessageInfo methodMessageInfo = MethodMessageInfo.CreateMethodMessageInfo<T, TResult>(expression, Target);
            CallSelector<IInterceptor>(i => i.Setup(methodMessageInfo));
            CallSelector<IDataProvider>(s => s.Setup(methodMessageInfo));
            CallSelector<IVerifier>(i => i.Setup(methodMessageInfo));
        }

        public void MockFuncMethod<TResult, TAction>(Expression<Func<T, TResult>> expression, TAction actionTarget)
            where TAction : IAction
        {            
            MethodMessageInfo methodMessageInfo = MethodMessageInfo.CreateMethodMessageInfo<T, TResult>(expression, Target);
            CallSelector<TAction>(i => i.Setup(methodMessageInfo, actionTarget));

            if (typeof(TAction) != typeof(IInterceptor))
                SetupDefaultAction<IInterceptor>(methodMessageInfo, false);
        }

        public void MockFuncMethod<TResult, TAction>(Expression<Func<T, TResult>> expression, Action<MethodMessageInfo, TAction> action)
            where TAction : IAction
        {            
            MethodMessageInfo methodMessageInfo = MethodMessageInfo.CreateMethodMessageInfo<T, TResult>(expression, Target);
            CallSelector<TAction>(s => s.Setup(methodMessageInfo));
            CallSelector<TAction>(s => s.CallDefaultAction(methodMessageInfo, action));

            if (typeof(TAction) != typeof(IInterceptor))
                SetupDefaultAction<IInterceptor>(methodMessageInfo, false);
        }

        public void MockFuncMethod<TResult, TAction>(Expression<Func<T, TResult>> expression, Func<MethodMessageInfo, IEnumerable<TAction>, TAction> selectFunc)
            where TAction : IAction
        {            
            MethodMessageInfo methodMessageInfo = MethodMessageInfo.CreateMethodMessageInfo<T, TResult>(expression, Target);
            CallSelector<TAction>(s => s.Setup(methodMessageInfo, selectFunc));

            if (typeof(TAction) != typeof(IInterceptor))
                SetupDefaultAction<IInterceptor>(methodMessageInfo, false);
        }
        #endregion

        #region mock all method of the class instance
        public void MockClass()
        {
            CallSelector<IInterceptor>(i => i.Setup(default(IInterceptor)));
            CallSelector<IDataProvider>(s => s.Setup(default(IDataProvider)));
            CallSelector<IVerifier>(i => i.Setup(default(IVerifier )));
        }

        public void MockClass<TAction>(TAction actionTarget)
            where TAction : IAction
        {
            CallSelector<TAction>(s => s.Setup(actionTarget));
        }
        #endregion
    }
}
